﻿using EventBus.Abstractions;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EventBus.Simple
{
    public class EventHandlerExecutionContext
        : IEventHandlerExecutionContext
    {
        private readonly IServiceCollection _registry;
        private readonly Func<IServiceCollection, IServiceProvider> _serviceFactory;
        private ConcurrentDictionary<Type, List<Type>> _registrations = new ConcurrentDictionary<Type, List<Type>>();
        public EventHandlerExecutionContext(IServiceCollection registry,
            Func<IServiceCollection, IServiceProvider> serviceFactory)
        {
            _registry = registry;
            _serviceFactory = _serviceFactory == null ? _serviceFactory : _registry => _registry.BuildServiceProvider();
        }
        public async Task HandleAsync(IEvent @event)
        {
            var type = @event.GetType();
            if (_registrations.TryGetValue(type, out List<Type> handlerTypes) && handlerTypes != null && handlerTypes.Any())
            {
                using (var scope = _serviceFactory(_registry).CreateScope())
                {
                    var sp = scope.ServiceProvider;
                    foreach (var item in handlerTypes)
                    {
                        var handler = (IEventHandler)sp.GetService(item);
                        if (handler.CanHandle(@event))
                        {
                            await handler.HandleAsync(@event);
                        }
                    }
                }
            }
        }

        public bool HandlerRegistered(Type eventType, Type handlerType)
        {
            return _registrations.TryGetValue(eventType, out List<Type> handlerTypes) && handlerTypes.Contains(handlerType);
        }

        public bool HandlerRegistered<TEvent, TEventHandler>()
            where TEvent : IEvent
            where TEventHandler : IEventHandler<TEvent>
        {
            return HandlerRegistered(typeof(TEvent), typeof(TEventHandler));
        }

        public void RegisterHandler(Type eventType, Type handlerType)
        {
            var isExisted = _registrations.TryGetValue(eventType, out List<Type> handlerTypes);
            if (!isExisted)
            {
                handlerTypes = new List<Type>();
            }
            handlerTypes.Add(handlerType);
            _registrations.TryAdd(eventType, handlerTypes);
            _registry.AddSingleton(handlerType);
        }

        public void RegisterHandler<TEvent, TEventHandler>()
            where TEvent : IEvent
            where TEventHandler : IEventHandler<TEvent>
        {
            RegisterHandler(typeof(TEvent), typeof(TEventHandler));
        }
    }
}
